package com.graphics.element;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.opengl.GLES30;
import android.opengl.GLUtils;

import com.math3d.MatrixUtil;
import com.system.Camera;
import com.system.Shader;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.List;

import static com.math3d.MatrixUtil.mat4_byte_length;

/**
 * @Author: clm
 * @Date: 2018-10-26
 */

public class SkyCube extends AbsSceneNode {
    private Camera mCamera;
    private List<Bitmap> mSkyImg;
    private float[] mTempMatrix;
    private int mGL_cube_map[];
    private int mSkyShader;
    private int mSkyVBO[] = new int[1];
    float skyboxVertices[] = {
            // positions
            -1.0f,  1.0f, -1.0f,
            -1.0f, -1.0f, -1.0f,
            1.0f, -1.0f, -1.0f,
            1.0f, -1.0f, -1.0f,
            1.0f,  1.0f, -1.0f,
            -1.0f,  1.0f, -1.0f,

            -1.0f, -1.0f,  1.0f,
            -1.0f, -1.0f, -1.0f,
            -1.0f,  1.0f, -1.0f,
            -1.0f,  1.0f, -1.0f,
            -1.0f,  1.0f,  1.0f,
            -1.0f, -1.0f,  1.0f,

            1.0f, -1.0f, -1.0f,
            1.0f, -1.0f,  1.0f,
            1.0f,  1.0f,  1.0f,
            1.0f,  1.0f,  1.0f,
            1.0f,  1.0f, -1.0f,
            1.0f, -1.0f, -1.0f,

            -1.0f, -1.0f,  1.0f,
            -1.0f,  1.0f,  1.0f,
            1.0f,  1.0f,  1.0f,
            1.0f,  1.0f,  1.0f,
            1.0f, -1.0f,  1.0f,
            -1.0f, -1.0f,  1.0f,

            -1.0f,  1.0f, -1.0f,
            1.0f,  1.0f, -1.0f,
            1.0f,  1.0f,  1.0f,
            1.0f,  1.0f,  1.0f,
            -1.0f,  1.0f,  1.0f,
            -1.0f,  1.0f, -1.0f,

            -1.0f, -1.0f, -1.0f,
            -1.0f, -1.0f,  1.0f,
            1.0f, -1.0f, -1.0f,
            1.0f, -1.0f, -1.0f,
            -1.0f, -1.0f,  1.0f,
            1.0f, -1.0f,  1.0f
    };
    public SkyCube(String name, Camera camera, List<Bitmap> skyImg){
        super(name);
        mCamera = camera;
        mTempMatrix = new float[16];
        mSkyImg = skyImg;
        init();
    }

    public void setSkyImg(List<Bitmap> skyImg) {
        mSkyImg = skyImg;
    }

    @SuppressLint("NewApi")
    public void init(){
        //shader
        Shader shader = new Shader("sky_vs.glsl", "sky_fs.glsl");
        mSkyShader = shader.mShaderProgram;
        //buffer
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(4 * skyboxVertices.length);
        FloatBuffer fb = byteBuffer.order(ByteOrder.nativeOrder()).asFloatBuffer();
        fb.put(skyboxVertices).position(0);
        GLES30.glGenBuffers(1, mSkyVBO, 0);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mSkyVBO[0]);
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, 4 * fb.limit(), fb, GLES30.GL_STATIC_DRAW);
        //int pos_loc = GLES30.glGetAttribLocation(mSkyShader, "pos");
        GLES30.glVertexAttribPointer(0x001, 3, GLES30.GL_FLOAT, false, 4 * 3, 0);
        GLES30.glEnableVertexAttribArray(0x001);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
        //纹理
        mGL_cube_map = new int[1];
        GLES30.glGenTextures(1, mGL_cube_map, 0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP, mGL_cube_map[0]);
        for(int i = 0;i < mSkyImg.size();i++){
            GLUtils.texImage2D(GLES30.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, mSkyImg.get(i), 0);
        }
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_CUBE_MAP, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_CUBE_MAP, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_CUBE_MAP, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_CUBE_MAP, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_CUBE_MAP, GLES30.GL_TEXTURE_WRAP_R, GLES30.GL_CLAMP_TO_EDGE);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP, 0);
        //数据初始化
        GLES30.glUniform1i(GLES30.glGetUniformLocation(mSkyShader, "cubeText"), 0);
        GLES30.glUseProgram(0);
        //统一uniform缓存
        ubo = new int[1];
        GLES30.glGenBuffers(1, ubo, 0);
        GLES30.glBindBuffer(GLES30.GL_UNIFORM_BUFFER, ubo[0]);
        GLES30.glBufferData(GLES30.GL_UNIFORM_BUFFER, 4 * 4 * 4 * 3, null, GLES30.GL_STATIC_DRAW);
        GLES30.glBindBufferRange(GLES30.GL_UNIFORM_BUFFER, 0x001, ubo[0], 0, 4 * 4 * 3);
        GLES30.glBindBuffer(GLES30.GL_UNIFORM_BUFFER, 0);
        GLES30.glUseProgram(mSkyShader);
        GLES30.glUniformBlockBinding(mSkyShader, GLES30.glGetUniformBlockIndex(mSkyShader, "MATRIX"), 0x001);
        GLES30.glUseProgram(0);
        pbf = ByteBuffer.allocateDirect(mat4_byte_length).order(ByteOrder.nativeOrder()).asFloatBuffer();
        pbf.put(mCamera.mP_matrix).position(0);
        vbf = ByteBuffer.allocateDirect(mat4_byte_length).order(ByteOrder.nativeOrder()).asFloatBuffer();
    }
    private int[] ubo;
    private FloatBuffer pbf;
    private FloatBuffer vbf;

    @Override
    public void update(float exTime) {
        //更新物体
    }

    @Override
    public final void render(float exTime){
        GLES30.glDepthFunc(GLES30.GL_LEQUAL);
        GLES30.glUseProgram(mSkyShader);
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP, mGL_cube_map[0]);
        MatrixUtil.mat4Copy(mTempMatrix, mCamera.mV_matrix);
        MatrixUtil.mat4CoverMat33(mTempMatrix);
        //GLES30.glUniformMatrix4fv(v_matrix_loc, 1, false, mTempMatrix, 0);
        //使用uniform缓存
        GLES30.glBindBuffer(GLES30.GL_UNIFORM_BUFFER, ubo[0]);
        vbf.clear();
        vbf.put(mTempMatrix).position(0);
        GLES30.glBufferSubData(GLES30.GL_UNIFORM_BUFFER, 0, mat4_byte_length, vbf);
        GLES30.glBufferSubData(GLES30.GL_UNIFORM_BUFFER, mat4_byte_length, mat4_byte_length, pbf);
        GLES30.glBindBuffer(GLES30.GL_UNIFORM_BUFFER, 0);


        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mSkyVBO[0]);
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 36);
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
        GLES30.glUseProgram(0);
        GLES30.glDepthFunc(GLES30.GL_LESS);
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        GLES30.glBindTexture(GLES30.GL_TEXTURE_CUBE_MAP, 0);
    }
}
